<?php
/**
 * TinyMVC - TinyMVCActionController.class.php file
 *
 * @author Adrian Wieczorek <adi(at)tinymvc(dot)org>
 * @package TinyMVC
 * @version $Id: TinyMVCActionController.class.php 75 2009-11-09 19:53:20Z adi3ek $
 *
 */

/**
 * TinyMVCActionController abstract class, module action controllers should inherit from this class, for example:
 *
 * <code>
 * <?php
 * class FooActionController extends TinyMVCActionController {
 *
 *  public function barAction() {
 *   // action logic here, this method will be called for request: /index.php?mod=foo&act=bar
 *  }
 *
 * }
 * ?>
 * </code>
 *
 * @package TinyMVC
 *
 */
abstract class TinyMVCActionController {
	/**
		* Transport object for Model and View (It's private, because we wan't get/set methods to be called instead of direct access)
		* @var TinyMVCModelAndView
		*/
	private $modelAndView = null;
	/**
		* Current action name
		* @var string
		*/
	protected $currentAction;
	/**
		* Model (alias: $modelAndView->getModel())
		* @var array
		*/
	protected $model;
	/**
		* View (alias: $modelAndView->getView())
		* @var array
		*/
	protected $view;
	/**
	 * Framework (alias: TinyMVC::getInstance())
	 */
	protected $framework;
	/**
		* Current module name
		* @var string
		*/
	protected $currentModule;
	/**
	 * Default action name
	 * @var string
	 */
	protected $defaultAction ;
	/**
	 * Request object
	 * @var TinyMVCRequest
	 */
	protected $request = null;

	/**
	 * this constructor need to be called in descendant classes
	 * @param TinyMVCModelAndView $modelAndView model and view transfer object
	 * @param TinyMVCRequest $request	request object
	 * @param string $defaultAction default action name
	 */
	public function __construct(TinyMVCModelAndView $modelAndView, TinyMVCRequest $request, $defaultAction) {
		$this->setModelAndView($modelAndView);
		$this->setRequest($request);
		$this->defaultAction = $defaultAction;
		$this->currentAction = $request->getParam('act') ? $request->getParam('act') : $this->defaultAction;
		$this->framework = TinyMVC::getInstance();
	}

	/**
		* find and execute correct action method
		* @param string $actionName action name (optional)
		* @return TinyMVCModelAndView transfer object
		*/
	public function doAction($actionName = "") {
		$actionMethod = strtolower(!empty($actionName) ? $actionName : $this->currentAction)."Action";

		if(method_exists($this, $actionMethod)) {
			$this->$actionMethod();
			return $this->modelAndView;
		}
		else {
			throw new TinyMVCException("Unknown action: " . $this->currentAction);
		}
	}

	/**
	 * perform redirect to given url
	 * @param string $url url
	 */
	public function doRedirect($url) {
		header('Location: ' . $url);
		exit;
	}

	/**
	 * get model and view
	 * @return TinyMVCModelAndView model and view object
	 */
	public function getModelAndView() {
		return $this->modelAndView;
	}

	/**
	 * set model and view object
	 * @param TinyMVCModelAndView $modelAndView model and view transfer object
	 *
	 */
	public function setModelAndView(TinyMVCModelAndView $modelAndView) {
		$this->modelAndView = $modelAndView;

		// setting aliases
		if(!$this->modelAndView->isCustomModel()) {
			// TODO: do we really need "elements" as separate container?
			$this->model = $this->modelAndView->getModel()->elements;
		}
		else {
			// custom Model in use, pass it "transparently"
			$this->model = $this->modelAndView->getModel();
		}
		$this->view = $this->modelAndView->getView();
	}

	/**
	 * get request object
	 * @return TinyMVCRequest request object
	 */
	public function getRequest() {
		return $this->request;
	}

	/**
	 * set request object
	 * @param TinyMVCRequest $request	request object
	 */
	public function setRequest(TinyMVCRequest $request) {
		$this->request = $request;
		$this->setCurrentModule($request->getParam("mod"));
	}

	/**
	 * get current module name
	 * @return string module name
	 */
	public function getCurrentModule() {
		return $this->currentModule;
	}

	/**
	 * set current module name
	 * @param string $moduleName module name
	 */
	public function setCurrentModule($moduleName) {
		$this->currentModule = $moduleName;
	}

	/**
	 * get default action
	 * @return string action name
	 */
	public function getDefaultAction() {
		return $this->defaultAction;
	}

	/**
	 * set default action name
	 * @param string $defaultAction action name
	 */
	public function setDefaultAction($defaultAction) {
		$this->defaultAction = $defaultAction;
	}

	/**
	 * get framework resource
	 * @param string $resourceName
	 * @return TinyMVCResource resource object
	 */
	public function getResource($resourceName) {
		return $this->framework->getResource($resourceName);
	}

	/**
	 * overloading __set() to prevent users from using undefined properties
	 * (yeah, we think it's bad idea using undefined class properties!)
	 *
	 *
	 */
	public function __set($key, $value) {
		throw new TinyMVCException("Trying to set undefined property: \"$key\" inside controller. Doing so it's generally bad idea, however if you're ok about that, simply overload TinyMVCActionController::__set() magic method in your controller.");
	}

	/**
	 * overloading __get() to prevent users from using undefined properties
	 * (yeah, we think it's bad idea using undefined class properties!)
	 */
	public function __get($key) {
		throw new TinyMVCException("Trying to get undefined property: \"$key\" inside controller. Doing so it's generally bad idea, however if you're ok about that, simply overload TinyMVCActionController::__get() magic method in your controller.");
	}

}
